/*
 ------------------------------------------------------------------------------
 Copyright (C) 2009-2011 Eternal Games.

 This file is part of the Eternal Tech source code.

 The Eternal Tech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Eternal Tech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Eternal Tech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// r_main.cpp - Initializes the renderer
//

// TODO:
// - Finish Crops and performance counters
// - Put commands into thier own systems


#include "r_local.h"


#define NUM_VIDEO_MODES					(sizeof(r_videoModes) / sizeof(videoMode_t))

struct videoMode_t {
	const char				*description;

	int						width;
	int						height;
};

static videoMode_t	r_videoModes[] = {
	{"Mode  0: 640x480 (4:3)",		640,	480},
	{"Mode  1: 800x600 (4:3)",		800,	600},
	{"Mode  2: 960x720 (4:3)",		960,	720},
	{"Mode  3: 1024x768 (4:3)",		1024,	768},
	{"Mode  4: 1152x864 (4:3)",		1152,	864},
	{"Mode  5: 1280x960 (4:3)",		1280,	960},
	{"Mode  6: 1400x1050 (4:3)",	1400,	1050},
	{"Mode  7: 1600x1200 (4:3)",	1600,	1200},
	{"Mode  8: 2048x1536 (4:3)",	2048,	1536},
	{"Mode  9: 720x576 (5:4)",		720,	576},
	{"Mode 10: 1280x1024 (5:4)",	1280,	1024},
	{"Mode 11: 640x360 (16:9)",		640,	360},
	{"Mode 12: 800x450 (16:9)",		800,	450},
	{"Mode 13: 960x540 (16:9)",		960,	540},
	{"Mode 14: 1024x576 (16:9)",	1024,	576},
	{"Mode 15: 1088x612 (16:9)",	1088,	612},
	{"Mode 16: 1280x720 (16:9)",	1280,	720},
	{"Mode 17: 1600x900 (16:9)",	1600,	900},
	{"Mode 18: 1920x1080 (16:9)",	1920,	1080},
	{"Mode 19: 2048x1152 (16:9)",	2048,	1152},
	{"Mode 20: 640x400 (16:10)",	640,	400},
	{"Mode 21: 800x500 (16:10)",	800,	500},
	{"Mode 22: 960x600 (16:10)",	960,	600},
	{"Mode 23: 1024x640 (16:10)",	1024,	640},
	{"Mode 24: 1152x720 (16:10)",	1152,	720},
	{"Mode 25: 1280x800 (16:10)",	1280,	800},
	{"Mode 26: 1440x900 (16:10)",	1440,	900},
	{"Mode 27: 1680x1050 (16:10)",	1680,	1050},
	{"Mode 28: 1920x1200 (16:10)",	1920,	1200}
};

reGlobals_t					rg;

cvar_t						*r_glDriver;
cvar_t						*r_mode;
cvar_t						*r_fullscreen;
cvar_t						*r_customWidth;
cvar_t						*r_customHeight;
cvar_t						*r_displayRefresh;
cvar_t						*r_multiSamples;
cvar_t						*r_swapInterval;

cvar_t						*r_logFile;
cvar_t						*r_ignoreGLErrors;
cvar_t						*r_clear;
cvar_t						*r_clearColor;
cvar_t						*r_frontBuffer;
cvar_t						*r_offsetFactor;
cvar_t						*r_offsetUnits;
cvar_t						*r_shadowOffsetFactor;
cvar_t						*r_shadowOffsetUnits;
cvar_t						*r_finish;

cvar_t						*r_screenFraction;
cvar_t						*r_subviewOnly;
cvar_t						*r_lockPVS;
cvar_t						*r_zNear;
cvar_t						*r_zFar;
cvar_t						*r_singleMaterial;
cvar_t						*r_singleEntity;
cvar_t						*r_singleLight;
cvar_t						*r_lightScale;
cvar_t						*r_gamma;
cvar_t						*r_contrast;
cvar_t						*r_brightness;
cvar_t						*r_shadows;

cvar_t						*r_roundTexturesDown;
cvar_t						*r_downSizeTextures;
cvar_t						*r_downSizeNormalTextures;
cvar_t						*r_maxTextureSize;
cvar_t						*r_maxNormalTextureSize;
cvar_t						*r_compressTextures;
cvar_t						*r_compressNormalTextures;
cvar_t						*r_textureFilter;
cvar_t						*r_textureAnisotropy;
cvar_t						*r_textureLODBias;

cvar_t						*r_showCluster;
cvar_t						*r_showCull;
cvar_t						*r_showScene;
cvar_t						*r_showSurfaces;
cvar_t						*r_showMeshes;
cvar_t						*r_showShadows;
cvar_t						*r_showInteractions;
cvar_t						*r_showLights;
cvar_t						*r_showDynamic;
cvar_t						*r_showDeforms;
cvar_t						*r_showPrimitives;
cvar_t						*r_showVertexBuffers;
cvar_t						*r_showTextureUsage;
cvar_t						*r_showRenderToTexture;

cvar_t						*r_showDepth;
cvar_t						*r_showOverdraw;
cvar_t						*r_showShadowCount;
cvar_t						*r_showLightCount;
cvar_t						*r_showLightVolumes;
cvar_t						*r_showLightScissors;
cvar_t						*r_showShadowTris;
cvar_t						*r_showShadowVolumes;
cvar_t						*r_showShadowSilhouettes;
cvar_t						*r_showTris;
cvar_t						*r_showNormals;
cvar_t						*r_showTextureVectors;
cvar_t						*r_showTangentSpace;
cvar_t						*r_showTextureCoords;
cvar_t						*r_showVertexColors;
cvar_t						*r_showBatchSize;   // TODO: not used
cvar_t						*r_showModelBounds;
cvar_t						*r_showColorMipLevels;

cvar_t						*r_skipVisibility;
cvar_t						*r_skipAreas;
cvar_t						*r_skipCulling;
cvar_t						*r_skipSuppress;
cvar_t						*r_skipLightScissors;
cvar_t						*r_skipDepthBounds;
cvar_t						*r_skipLights;
cvar_t						*r_skipBump;
cvar_t						*r_skipDiffuse;
cvar_t						*r_skipSpecular;
cvar_t						*r_skipCopyToTextures;
cvar_t						*r_skipDynamicTextures;


/*
 ==============================================================================

    FUNCTIONS EXPORTED

 ==============================================================================
*/


/*
 ==================
 R_GetGLConfig
 ==================
*/
glConfig_t R_GetGLConfig ()
{
	return glConfig;
}

/*
 ==================
 R_Activate

 Called when the main window gains or loses focus.
 The window may have been destroyed and recreated between a deactivate and an
 activate.
 ==================
*/
void R_Activate (bool active)
{
	if (!glConfig.initialized)
		return;

	GLImp_Activate(active);
}

/*
 ==================
 R_GetVideoModeInfo
 ==================
*/
bool R_GetVideoModeInfo (int mode, int *width, int *height)
{
	if (mode == -1)
	{
		*width = r_customWidth->integerValue;
		*height = r_customHeight->integerValue;

		return true;
	}

	if (mode < 0 || mode >= NUM_VIDEO_MODES)
		return false;

	*width = r_videoModes[mode].width;
	*height = r_videoModes[mode].height;

	return true;
}

/*
 ==================
 R_SetLightStyle
 ==================
*/
void R_SetLightStyle (int style, float r, float g, float b)
{
	lightStyle_t	*ls;

	if (style < 0 || style >= MAX_LIGHTSTYLES)
		Com_Error(ERR_DROP, "R_SetLightStyle: out of range");

	ls = &rg.lightStyles[style];

	VectorSet(ls->rgb, r, g, b);
}


/*
 ==============================================================================

    RENDERING COMMANDS

 ==============================================================================
*/


/*
 ==================
 R_AddRenderViewCommand
 ==================
*/
void R_AddRenderViewCommand ()
{
	renderViewCommand_t   *cmd;

	cmd = (renderViewCommand_t *)RB_GetCommandBuffer(sizeof(*cmd));
	if (!cmd)
		return;

	cmd->commandId = RC_RENDER_VIEW;

	cmd->time = rg.renderView.time;
	memcpy(cmd->materialParms, rg.renderView.materialParms, MAX_MATERIAL_PARMS * sizeof(float));

	// Viewport
	cmd->viewport.x = rg.renderViewParms.viewportX;
	cmd->viewport.y = rg.renderViewParms.viewportY;
	cmd->viewport.width = rg.renderViewParms.viewportWidth;
	cmd->viewport.height = rg.renderViewParms.viewportHeight;

	// View parms
	cmd->viewParms.primaryView = rg.renderViewParms.primaryView;

	cmd->viewParms.subview = rg.renderViewParms.subview;

	VectorCopy(rg.renderView.viewOrigin, cmd->viewParms.origin);
	AxisCopy(rg.renderView.viewAxis, cmd->viewParms.axis);

	Matrix4_Copy(rg.renderViewParms.perspectiveMatrix, cmd->viewParms.perspectiveMatrix);
	Matrix4_Copy(rg.renderViewParms.worldMatrix, cmd->viewParms.worldMatrix);
	Matrix4_Copy(rg.renderViewParms.skyBoxMatrix, cmd->viewParms.skyBoxMatrix);
	Matrix4_Copy(rg.renderViewParms.mirrorMatrix, cmd->viewParms.mirrorMatrix);

	// Mesh lists
	cmd->meshes = rg.renderViewParms.meshes;
	cmd->numMeshes = rg.renderViewParms.numMeshes;

	cmd->postProcessMeshes = rg.renderViewParms.postProcessMeshes;
	cmd->numPostProcessMeshes = rg.renderViewParms.numPostProcessMeshes;

	// Light lists
	cmd->lights = rg.renderViewParms.lights;
	cmd->numLights = rg.renderViewParms.numLights;

	cmd->fogLights = rg.renderViewParms.fogLights;
	cmd->numFogLights = rg.renderViewParms.numFogLights;
}

/*
 ==================
 R_DrawStretchPic

 TODO: Add new aspect ratio stuff
 ==================
*/
void R_DrawStretchPic (float x, float y, float w, float h, float s1, float t1, float s2, float t2, const color_t color, material_t *material)
{
	stretchPicCommand_t	*cmd;

	if (!material)
		Com_Error(ERR_DROP, "R_DrawStretchPic: NULL material");

	// Adjust coordinates
	x *= rg.currentRenderCrop->xScale;
	y *= rg.currentRenderCrop->yScale;
	w *= rg.currentRenderCrop->xScale;
	h *= rg.currentRenderCrop->yScale;

	// Add a stretch pic command
	cmd = (stretchPicCommand_t *)RB_GetCommandBuffer(sizeof(*cmd));
	if (!cmd)
		return;

	cmd->commandId = RC_STRETCH_PIC;

	cmd->x = x;
	cmd->y = y;
	cmd->w = w;
	cmd->h = h;
	cmd->s1 = s1;
	cmd->t1 = t1;
	cmd->s2 = s2;
	cmd->t2 = t2;

	cmd->color[0] = color[0];
	cmd->color[1] = color[1];
	cmd->color[2] = color[2];
	cmd->color[3] = color[3];

	cmd->material = material;
}

/*
 ==================
 R_DrawShearedPic

 TODO: Add new aspect ratio stuff
 ==================
*/
void R_DrawShearedPic (float x, float y, float w, float h, float s1, float t1, float s2, float t2, float shearX, float shearY, const color_t color, material_t *material)
{
	shearedPicCommand_t	*cmd;

	if (!material)
		Com_Error(ERR_DROP, "R_DrawShearedPic: NULL material");

	// Adjust coordinates
	x *= rg.currentRenderCrop->xScale;
	y *= rg.currentRenderCrop->yScale;
	w *= rg.currentRenderCrop->xScale;
	h *= rg.currentRenderCrop->yScale;

	shearX *= rg.currentRenderCrop->xScale;
	shearY *= rg.currentRenderCrop->yScale;

	// Add a sheared pic command
	cmd = (shearedPicCommand_t *)RB_GetCommandBuffer(sizeof(*cmd));
	if (!cmd)
		return;

	cmd->commandId = RC_SHEARED_PIC;

	cmd->x = x;
	cmd->y = y;
	cmd->w = w;
	cmd->h = h;
	cmd->s1 = s1;
	cmd->t1 = t1;
	cmd->s2 = s2;
	cmd->t2 = t2;
	cmd->shearX = shearX;
	cmd->shearY = shearY;

	cmd->color[0] = color[0];
	cmd->color[1] = color[1];
	cmd->color[2] = color[2];
	cmd->color[3] = color[3];

	cmd->material = material;
}

/*
 ==================
 R_CropRender

 TODO: Extend to support new aspect stuff
 ==================
*/
void R_CropRender (int width, int height, bool makePowerOfTwo)
{
	renderSizeCommand_t   *cmd;

	if (rg.numRenderCrops == MAX_RENDER_CROPS)
		Com_Error(ERR_DROP, "R_CropRender: MAX_RENDER_CROPS hit");

	rg.numRenderCrops++;

	// Check the dimensions
	if ((width < 1 || width > 640) || (height < 1 || height > 480))
		Com_Error(ERR_DROP, "R_CropRender: bad size (%i x %i)", width, height);

	// Calculate the new crop size
	width *= rg.currentRenderCrop->xScale;
	height *= rg.currentRenderCrop->yScale;

	if (makePowerOfTwo)
	{
		width = NearestPowerOfTwo(width, true);
		height = NearestPowerOfTwo(height, true);
	}

	// Set up the current render crop
	rg.currentRenderCrop = &rg.renderCrops[rg.numRenderCrops-1];

	rg.currentRenderCrop->width = width;
	rg.currentRenderCrop->height = height;

	rg.currentRenderCrop->xScale = width / 640.0f;
	rg.currentRenderCrop->yScale = height / 480.0f;

	// Add a render size command
	cmd = (renderSizeCommand_t *)RB_GetCommandBuffer(sizeof(*cmd));
	if (!cmd)
		return;

	cmd->commandId = RC_RENDER_SIZE;

	cmd->width = rg.currentRenderCrop->width;
	cmd->height = rg.currentRenderCrop->height;
}

/*
 ==================
 R_UnCropRender
 ==================
*/
void R_UnCropRender ()
{
	renderSizeCommand_t   *cmd;

	if (rg.numRenderCrops < 1)
		Com_Error(ERR_DROP, "R_UnCropRender: numRenderCrops < 1");

	rg.numRenderCrops--;

	// Set up the current render crop
	rg.currentRenderCrop = &rg.renderCrops[rg.numRenderCrops-1];

	// Add a render size command
	cmd = (renderSizeCommand_t *)RB_GetCommandBuffer(sizeof(*cmd));
	if (!cmd)
		return;

	cmd->commandId = RC_RENDER_SIZE;

	cmd->width = rg.currentRenderCrop->width;
	cmd->height = rg.currentRenderCrop->height;
}

/*
 ==================
 R_SetCropSize

 TODO: Add a RC_RENDER_SIZE command?
 ==================
*/
static void R_SetCropSize ()
{
	int     width, height;
	int		size;

	width = mathMain::FloatToInt(glConfig.videoWidth * r_screenFraction->floatValue) & ~1;
	height = mathMain::FloatToInt(glConfig.videoHeight * r_screenFraction->floatValue) & ~1;

	// Clear render crops
	rg.numRenderCrops = 1;

	// Set up the current render crop
	rg.currentRenderCrop = &rg.renderCrops[rg.numRenderCrops-1];

	rg.currentRenderCrop->width = width;
	rg.currentRenderCrop->height = height;

	rg.currentRenderCrop->xScale = width * (1.0f / SCREEN_WIDTH);
	rg.currentRenderCrop->yScale = height * (1.0f / SCREEN_HEIGHT);

	if (width * SCREEN_HEIGHT > height * SCREEN_WIDTH)
	{
		size = height * SCREEN_WIDTH / SCREEN_HEIGHT;

		rg.currentRenderCrop->aspectRatio = ASPECT_WIDE;
		rg.currentRenderCrop->aspectScale = size * (1.0f / SCREEN_WIDTH);
		rg.currentRenderCrop->aspectBias = (width - size) * 0.5f;
	}
	else if (width * SCREEN_HEIGHT < height * SCREEN_WIDTH)
	{
		size = width * SCREEN_HEIGHT / SCREEN_WIDTH;

		rg.currentRenderCrop->aspectRatio = ASPECT_HIGH;
		rg.currentRenderCrop->aspectScale = size * (1.0f / SCREEN_HEIGHT);
		rg.currentRenderCrop->aspectBias = (height - size) * 0.5f;
	}
	else
	{
		rg.currentRenderCrop->aspectRatio = ASPECT_NORMAL;
		rg.currentRenderCrop->aspectScale = height * (1.0f / SCREEN_HEIGHT);
		rg.currentRenderCrop->aspectBias = 0.0f;
	}

	// Clear primary view
	rg.primaryViewAvailable = false;
}


/*
 ==============================================================================

    PERFORMANCE COUNTERS RENDERING

 ==============================================================================
*/

// TODO: Get rid of these and move them to somewhere else
void CL_DrawString (float x, float y, float size, const char *string, const color_t color, fontSet_t *font, int flags);
void CL_DrawPic (float x, float y, float w, float h, const color_t color, struct material_t *material);


/*
 ==================
 R_PerformanceCounters

 TODO: Finish!
 ==================
*/
void R_PerformanceCounters ()
{
	byte	headLineColor[4] = {0, 128, 0, 128};
	byte	color[4] = {128, 128, 128, 128};
	char	string[256], string2[256], string3[256], string4[256];

	if (r_showCull->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "World: bin: %i, bout: %i, sin: %i, sout: %i, bclip: %i, sclip: %i\n", rg.pc.worldBoxIn, rg.pc.worldBoxOut, rg.pc.worldSphereIn, rg.pc.worldSphereOut, rg.pc.worldBoxClip, rg.pc.worldSphereClip);
		Str_SPrintf(string2, sizeof(string2), "Entities: bin: %i, bout: %i, sin: %i, sout: %i, bclip: %i, sclip: %i\n", rg.pc.entityBoxIn, rg.pc.entityBoxOut, rg.pc.entitySphereIn, rg.pc.entitySphereOut, rg.pc.entityBoxClip, rg.pc.entitySphereClip);
		Str_SPrintf(string3, sizeof(string3), "Lights: bin: %i, bout: %i, sin: %i, sout: %i, bclip: %i, sclip: %i\n", rg.pc.lightBoxIn, rg.pc.lightBoxOut, rg.pc.lightSphereIn, rg.pc.lightSphereOut, rg.pc.lightBoxClip, rg.pc.lightSphereClip);
		Str_SPrintf(string4, sizeof(string4), "Light Ents: bin: %i, bout: %i, sin: %i, sout: %i, bclip: %i, sclip: %i\n", rg.pc.lightEntBoxIn, rg.pc.lightEntBoxOut, rg.pc.lightEntSphereIn, rg.pc.lightEntSphereOut, rg.pc.lightEntBoxClip, rg.pc.lightEntSphereClip);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "Box And Sphere Culling", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 29.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 38.0f, 0.16f, string2, colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 41.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 50.0f, 0.16f, string3, colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 53.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 62.0f, 0.16f, string4, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showScene->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "entities: %i, decals: %i, polys: %i", rg.pc.entities, rg.pc.decals, rg.pc.polys);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "Elements Present In A Scene", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showSurfaces->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "surfaces: %i, leafs: %i", rg.pc.surfaces, rg.pc.leafs);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "Surfaces And Leafs Drawn", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showMeshes->integerValue)
	{
	}

	if (r_showShadows->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "shadows: %i (zpass: %i, zfail: %i)", rg.pc.shadows, rg.pc.shadowsZPass, rg.pc.shadowsZFail);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "Shadows Interaction Passes", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showInteractions->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "interactions: %i (light: %i, fog: %i)", rg.pc.interactions, rg.pc.interactionsLight, rg.pc.interactionsFog);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "Light Interaction Passes", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showLights->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "lights: %i (static: %i, dynamic: %i)", rg.pc.lights, rg.pc.staticLights, rg.pc.dynamicLights);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "Light Passes", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showDeforms->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "deforms: %i (deformVertices: %i)", rg.pc.deforms, rg.pc.deformVertices);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "TODO", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showPrimitives->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "views: %i, draws: %i, tris: %i (shdw: %i), verts: %i (shdw: %i)", rg.pc.views, rg.pc.draws, (rg.pc.indices + rg.pc.shadowIndices) / 3, rg.pc.shadowIndices / 3, (rg.pc.vertices + rg.pc.shadowVertices), rg.pc.shadowVertices);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "TODO", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showVertexBuffers->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "vertex buffers: %i (%.2f MB)", rg.pc.vertexBuffers, rg.pc.vertexBufferBytes / 1048576.0f);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "TODO", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showTextureUsage->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "textures: %i (%.2f MB)", rg.pc.textures, rg.pc.textureBytes / 1048576.0f);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "TODO", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showRenderToTexture->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "capture renders: %i (%ik pixels), update textures: %i (%ik pixels)", rg.pc.captureRenders, rg.pc.captureRenderPixels / 1000, rg.pc.updateTextures, rg.pc.updateTexturePixels / 1000);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "TODO", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showOverdraw->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "overdraw: %.2f", rg.pc.overdraw);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "TODO", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}

	if (r_showLightCount->integerValue)
	{
		Str_SPrintf(string, sizeof(string), "light overdraw: %.2f", rg.pc.overdrawLights);

		CL_DrawPic(5.0f, 5.0f, 256, 12, headLineColor, NULL);
		CL_DrawString(6.0f, 14.0f, 0.16f, "TODO", colorTable[COLOR_WHITE], NULL, 0);

		CL_DrawPic(5.0f, 17.0f, 256, 12, color, NULL);
		CL_DrawString(6.0f, 27.0f, 0.16f, string, colorTable[COLOR_WHITE], NULL, 0);
	}
}


/*
 ==============================================================================

    CLEARING AND DEVICE GAMMA RAMP

 ==============================================================================
*/


/*
 ==================
 R_ClearRender

 Used for updating data and prevents overflow
 ==================
*/
static void R_ClearRender ()
{
	int		i;

	memset(&rg, 0, sizeof(reGlobals_t));

	// Clear counters
	rg.frameCount = 1;
	rg.viewCount = 1;
	rg.visCount = 1;
	rg.worldCount = 1;
	rg.lightCount = 1;
	rg.fragmentCount = 1;

	// Clear view clusters
	rg.viewCluster = rg.oldViewCluster = -1;
	rg.viewCluster2 = rg.oldViewCluster2 = -1;

	// Clear light styles
	for (i = 0; i < MAX_LIGHTSTYLES; i++)
	{
		rg.lightStyles[i].rgb[0] = 1.0f;
		rg.lightStyles[i].rgb[1] = 1.0f;
		rg.lightStyles[i].rgb[2] = 1.0f;
	}

	// Clear decals
	//R_ClearDecals();
}

/*
 ==================
 R_SetGamma
 ==================
*/
static void R_SetGamma ()
{
	float	f;
	int		i;

	// Build gamma table
	for (i = 0; i < 256; i++)
	{
		f = i * (1.0f / 255.0f);

		// Adjust gamma
		f = mathMain::Pow(f, 1.0f / r_gamma->floatValue);

		// Adjust contrast
		f = (f - 0.5f) * r_contrast->floatValue + 0.5f;

		// Adjust brightness
		if (r_brightness->floatValue < 1.0f)
			f = f + (r_brightness->floatValue - 1.0f);
		else
			f = f + (r_brightness->floatValue - 1.0f) * 0.5f;

		// Clamp the final value
		rg.gammaTable[i] = mathMain::FloatToByte(f * 255.0f);
	}

	// Set device gamma ramp
	GLImp_SetDeviceGammaRamp(rg.gammaTable);
}


/*
 ==============================================================================

    CONSOLE COMMANDS

 ==============================================================================
*/


/*
 ==================
 R_GfxInfo_f
 ==================
*/
static void R_GfxInfo_f ()
{
	Com_Printf("\n");
	Com_Printf("GL_VENDOR: %s\n", glConfig.vendorString);
	Com_Printf("GL_RENDERER: %s\n", glConfig.rendererString);
	Com_Printf("GL_VERSION: %s\n", glConfig.versionString);
	Com_Printf("GL_EXTENSIONS: %s\n", glConfig.extensionsString);
	Com_Printf("WGL_EXTENSIONS: %s\n", glConfig.wglExtensionsString);
	Com_Printf("\n");
	Com_Printf("GL_SHADING_LANGUAGE_VERSION: %s\n", glConfig.shadingLanguageVersionString);
	Com_Printf("\n");
	Com_Printf("GL_MAX_TEXTURE_SIZE: %i\n", glConfig.maxTextureSize);
	Com_Printf("GL_MAX_3D_TEXTURE_SIZE: %i\n", glConfig.max3DTextureSize);
	Com_Printf("GL_MAX_CUBE_MAP_TEXTURE_SIZE: %i\n", glConfig.maxCubeMapTextureSize);
	Com_Printf("GL_MAX_ARRAY_TEXTURE_LAYERS: %i\n", glConfig.maxArrayTextureLayers);
	Com_Printf("GL_MAX_TEXTURE_UNITS: %i\n", glConfig.maxTextureUnits);
	Com_Printf("GL_MAX_TEXTURE_COORDS: %i\n", glConfig.maxTextureCoords);
	Com_Printf("GL_MAX_TEXTURE_IMAGE_UNITS: %i\n", glConfig.maxTextureImageUnits);
	Com_Printf("GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: %i\n", glConfig.maxVertexTextureImageUnits);
	Com_Printf("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %i\n", glConfig.maxCombinedTextureImageUnits);
	Com_Printf("GL_MAX_VERTEX_ATTRIBS: %i\n", glConfig.maxVertexAttribs);
	Com_Printf("GL_MAX_VARYING_COMPONENTS: %i\n", glConfig.maxVaryingComponents);
	Com_Printf("GL_MAX_VERTEX_UNIFORM_COMPONENTS: %i\n", glConfig.maxVertexUniformComponents);
	Com_Printf("GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: %i\n", glConfig.maxFragmentUniformComponents);
	Com_Printf("GL_MAX_COLOR_ATTACHMENTS: %i\n", glConfig.maxColorAttachments);
	Com_Printf("GL_MAX_RENDERBUFFER_SIZE: %i\n", glConfig.maxRenderbufferSize);
	Com_Printf("GL_MAX_TEXTURE_LOD_BIAS: %.1f\n", glConfig.maxTextureLODBias);
	Com_Printf("GL_MAX_TEXTURE_MAX_ANISOTROPY: %.1f\n", glConfig.maxTextureMaxAnisotropy);
	Com_Printf("\n");
	Com_Printf("MODE: %i, %ix%i %s%s\n", r_mode->integerValue, glConfig.videoWidth, glConfig.videoHeight, (glConfig.isFullscreen) ? "fullscreen" : "windowed", (glConfig.displayFrequency) ? Str_VarArgs(" (%i Hz)", glConfig.displayFrequency) : "");
	Com_Printf("PIXELFORMAT: color(%i-bits) alpha(%i-bits) depth(%i-bits) stencil(%i-bits)\n", glConfig.colorBits, glConfig.alphaBits, glConfig.depthBits, glConfig.stencilBits);
	Com_Printf("SAMPLES: %s\n", (glConfig.multiSamples) ? Str_VarArgs("%ix", glConfig.multiSamples) : "none");
	Com_Printf("CPU: %s\n", Win_GetProcessorString());
	Com_Printf("\n");

	if (glConfig.textureFilterAnisotropic)
		Com_Printf("Using %s texture filtering w/ %.1f anisotropy\n", r_textureFilter->value, r_textureAnisotropy->floatValue);
	else
		Com_Printf("Using %s texture filtering\n", r_textureFilter->value);

	if (glConfig.textureCompression)
	{
		if (r_compressTextures->integerValue || r_compressNormalTextures->integerValue)
			Com_Printf("Using texture compression\n");
		else
			Com_Printf("Texture compression available but disabled\n");
	}
	else
		Com_Printf("Texture compression not available\n");

	if (glConfig.vertexBufferObject)
		Com_Printf("Using vertex buffers\n");
	else
		Com_Printf("Vertex buffers not available\n");

	if (glConfig.vertexShader)
		Com_Printf("Using vertex shaders\n");
	else
		Com_Printf("Vertex shaders not available\n");

	if (glConfig.fragmentShader)
		Com_Printf("Using fragment shaders\n");
	else
		Com_Printf("Fragment shaders not available\n");

	if (glConfig.stencilTwoSide || glConfig.atiSeparateStencil)
	{
		if (glConfig.atiSeparateStencil)
			Com_Printf("Using ATI seperate two sided stencil\n");
		else
			Com_Printf("Using two sided stencil\n");
	}
	else
		Com_Printf("Two sided stencil not available\n");

	if (glConfig.depthBoundsTest)
		Com_Printf("Using depth bounds testing\n");
	else
		Com_Printf("Depth bounds testing not available\n");

	if (glConfig.swapControl && r_swapInterval->integerValue)
		Com_Printf("Forcing swapInterval %i\n", r_swapInterval->integerValue);
	else
		Com_Printf("swapInterval not forced\n");

	if (r_finish->integerValue)
		Com_Printf("Forcing glFinish\n");
	else
		Com_Printf("glFinish not forced\n");

	Com_Printf("\n");
}

/*
 ==================
 R_ListVideoModes_f
 ==================
*/
static void R_ListVideoModes_f ()
{
	int		i;

	Com_Printf("Mode -1: %ix%i (custom)\n", r_customWidth->integerValue, r_customHeight->integerValue);

	for (i = 0; i < NUM_VIDEO_MODES; i++)
		Com_Printf("%s\n", r_videoModes[i].description);
}


/*
 ==============================================================================

    CONFIG REGISTRATION

 ==============================================================================
*/


/*
 ==================
 R_RegisterConfig
 ==================
*/
static void R_RegisterConfig ()
{
	// GL window variables
	r_glDriver = CVar_Register("r_glDriver", "", CVAR_STRING, CVAR_ARCHIVE | CVAR_LATCH, "GL driver", 0, 0);
	r_mode = CVar_Register("r_mode", "16", CVAR_INTEGER, CVAR_ARCHIVE | CVAR_LATCH, "Video mode index (-1 = custom)", -1, NUM_VIDEO_MODES - 1); // FIXME: set default to 0
	r_fullscreen = CVar_Register("r_fullscreen", "0", CVAR_BOOL, CVAR_ARCHIVE | CVAR_LATCH, "Enable fullscreen video mode", 0, 0);
	r_customWidth = CVar_Register("r_customWidth", "720", CVAR_INTEGER, CVAR_ARCHIVE | CVAR_LATCH, "Custom screen width", 256, 65536);
	r_customHeight = CVar_Register("r_customHeight", "480", CVAR_INTEGER, CVAR_ARCHIVE | CVAR_LATCH, "Custom screen height", 256, 65536);
	r_displayRefresh = CVar_Register("r_displayRefresh", "0", CVAR_INTEGER, CVAR_ARCHIVE | CVAR_LATCH, "Optional display refresh rate", 0, 1000);
	r_multiSamples = CVar_Register("r_multiSamples", "0", CVAR_INTEGER, CVAR_ARCHIVE | CVAR_LATCH, "Number of samples for multisample antialiasing", 0, 256);
	r_swapInterval = CVar_Register("r_swapInterval", "0", CVAR_INTEGER, CVAR_ARCHIVE, "Sync to the display's refresh rate", 0, 10);

	// GL variables
	r_logFile = CVar_Register("r_logFile", "0", CVAR_INTEGER, CVAR_CHEAT, "Logs all GL functions used every frame", 0, 0);
	r_ignoreGLErrors = CVar_Register("r_ignoreGLErrors", "1", CVAR_BOOL, CVAR_CHEAT, "Ignore GL errors", 0, 0);
	r_clear = CVar_Register("r_clear", "0", CVAR_BOOL, CVAR_CHEAT, "Clear the color buffer", 0, 0);
	r_clearColor = CVar_Register("r_clearColor", "0", CVAR_INTEGER, CVAR_CHEAT, "Clear color index", 0, COLOR_MASK);
	r_frontBuffer = CVar_Register("r_frontBuffer", "0", CVAR_BOOL, CVAR_CHEAT, "Render to the front buffer for debugging", 0, 0);
	r_offsetFactor = CVar_Register("r_offsetFactor", "-1.0", CVAR_FLOAT, CVAR_CHEAT, "Polygon offset factor", 0.0f, 0.0f); 
	r_offsetUnits = CVar_Register("r_offsetUnits", "-2.0", CVAR_FLOAT, CVAR_CHEAT, "Polygon offset units", 0.0f, 0.0f);
	r_shadowOffsetFactor = CVar_Register("r_shadowOffsetFactor", "0.0", CVAR_FLOAT, CVAR_CHEAT, "Shadow polygon offset factor", 0.0f, 0.0f);
	r_shadowOffsetUnits = CVar_Register("r_shadowOffsetUnits", "1.0", CVAR_FLOAT, CVAR_CHEAT, "Shadow polygon offset units", 0.0f, 0.0f);
	r_finish = CVar_Register("r_finish", "0", CVAR_BOOL, CVAR_ARCHIVE, "Synchronize CPU and GPU every frame", 0, 0);

	// Misc renderer variables
	r_screenFraction = CVar_Register("r_screenFraction", "1.0", CVAR_FLOAT, CVAR_CHEAT, "Render to a fraction of the screen for testing fillrate", 0.1f, 1.0f);
	r_subviewOnly = CVar_Register("r_subviewOnly", "0", CVAR_BOOL, CVAR_CHEAT, "Only render subviews for debugging", 0, 0);
	r_lockPVS = CVar_Register("r_lockPVS", "0", CVAR_BOOL, CVAR_CHEAT, "Don't update potential visibility sets when the camera moves", 0, 0);
	r_zNear = CVar_Register("r_zNear", "3.0", CVAR_FLOAT, CVAR_CHEAT, "Near clip plane distance", 0.01f, 10.0f);
	r_zFar = CVar_Register("r_zFar", "0.0", CVAR_FLOAT, CVAR_CHEAT, "Far clip plane distance (0 = dynamic)", 0.0f, 0.0f);
	r_singleMaterial = CVar_Register("r_singleMaterial", "0", CVAR_BOOL, CVAR_CHEAT | CVAR_LATCH, "Use a single default material on every mesh", 0, 0);
	r_singleEntity = CVar_Register("r_singleEntity", "-1", CVAR_INTEGER, CVAR_CHEAT, "Only draw the specified entity", -1, MAX_ENTITIES - 1);
	r_singleLight = CVar_Register("r_singleLight", "-1", CVAR_INTEGER, CVAR_CHEAT, "Only draw the specified light", -1, MAX_ENTITIES - 1);
	r_lightScale = CVar_Register("r_lightScale", "2.0", CVAR_FLOAT, CVAR_ARCHIVE, "Light intensity scale factor", 0.5f, 5.0f);
	r_gamma = CVar_Register("r_gamma", "1.0", CVAR_FLOAT, CVAR_ARCHIVE, "Adjust display gamma", 0.5f, 3.0f);
	r_contrast = CVar_Register("r_contrast", "1.0", CVAR_FLOAT, CVAR_ARCHIVE, "Adjust display contrast", 0.5f, 2.0f);
	r_brightness = CVar_Register("r_brightness", "1.0", CVAR_FLOAT, CVAR_ARCHIVE, "Adjust display brightness", 0.5f, 2.0f);
	r_shadows = CVar_Register("r_shadows", "1", CVAR_BOOL, CVAR_ARCHIVE, "Render stencil shadows", 0, 0);

	r_roundTexturesDown = CVar_Register("r_roundTexturesDown", "0", CVAR_BOOL, CVAR_ARCHIVE | CVAR_LATCH, "Down size non-power of two textures", 0, 0);
	r_downSizeTextures = CVar_Register("r_downSizeTextures", "0", CVAR_BOOL, CVAR_ARCHIVE | CVAR_LATCH, "Down size textures", 0, 0);
	r_downSizeNormalTextures = CVar_Register("r_downSizeNormalTextures", "0", CVAR_BOOL, CVAR_ARCHIVE | CVAR_LATCH, "Down size normal map textures", 0, 0);
	r_maxTextureSize = CVar_Register("r_maxTextureSize", "1024", CVAR_INTEGER, CVAR_ARCHIVE | CVAR_LATCH, "Maximum texture size", 256, 4096);
	r_maxNormalTextureSize = CVar_Register("r_maxNormalTextureSize", "512", CVAR_INTEGER, CVAR_ARCHIVE | CVAR_LATCH, "Maximum size for down sized normal map textures", 256, 4096);
	r_compressTextures = CVar_Register("r_compressTextures", "1", CVAR_BOOL, CVAR_ARCHIVE | CVAR_LATCH, "Compress textures", 0, 0);
	r_compressNormalTextures = CVar_Register("r_compressNormalTextures", "1", CVAR_BOOL, CVAR_ARCHIVE | CVAR_LATCH, "Compress normal map textures", 0, 0);
	r_textureFilter = CVar_Register("r_textureFilter", "GL_LINEAR_MIPMAP_LINEAR", CVAR_STRING, CVAR_ARCHIVE, "Filtering mode for mipmapped textures", 0, 0);
	r_textureLODBias = CVar_Register("r_textureLODBias", "0.0", CVAR_FLOAT, CVAR_ARCHIVE, "LOD bias for mipmapped textures", 0.0f, 0.0f);
	r_textureAnisotropy = CVar_Register("r_textureAnisotropy", "1.0", CVAR_FLOAT, CVAR_ARCHIVE, "Anisotropic filtering level for mipmapped textures", 0.0f, 0.0f);

	// Performance counter variables
	r_showCluster = CVar_Register("r_showCluster", "0", CVAR_BOOL, CVAR_CHEAT, "Show the current view cluster", 0, 0);
	r_showCull = CVar_Register("r_showCull", "0", CVAR_BOOL, CVAR_CHEAT, "Show culling statistics", 0, 0);
	r_showScene = CVar_Register("r_showScene", "0", CVAR_BOOL, CVAR_CHEAT, "Show number of entities, lights, particles, and decals in view", 0, 0);
	r_showSurfaces = CVar_Register("r_showSurfaces", "0", CVAR_BOOL, CVAR_CHEAT, "Show number of surfaces in view", 0, 0);
	r_showMeshes = CVar_Register("r_showMeshes", "0", CVAR_BOOL, CVAR_CHEAT, "Show number of meshes in view", 0, 0);
	r_showShadows = CVar_Register("r_showShadows", "0", CVAR_BOOL, CVAR_CHEAT, "Show number of shadows in view", 0, 0);
	r_showInteractions = CVar_Register("r_showInteractions", "0", CVAR_BOOL, CVAR_CHEAT, "Show number of interactions in view", 0, 0);
	r_showLights = CVar_Register("r_showLights", "0", CVAR_BOOL, CVAR_CHEAT, "Show number of lights in view", 0, 0);
	r_showDynamic = CVar_Register("r_showDynamic", "0", CVAR_BOOL, CVAR_CHEAT, "Show dynamic mesh generation statistics", 0, 0);
	r_showDeforms = CVar_Register("r_showDeforms", "0", CVAR_BOOL, CVAR_CHEAT, "Show material deform statistics", 0, 0);
	r_showPrimitives = CVar_Register("r_showPrimitives", "0", CVAR_BOOL, CVAR_CHEAT, "Show view/draw/index/vertex counts", 0, 0);
	r_showVertexBuffers = CVar_Register("r_showVertexBuffers", "0", CVAR_BOOL, CVAR_CHEAT, "Show vertex buffer usage", 0, 0);
	r_showTextureUsage = CVar_Register("r_showTextureUsage", "0", CVAR_BOOL, CVAR_CHEAT, "Show texture memory usage", 0, 0);
	r_showRenderToTexture = CVar_Register("r_showRenderToTexture", "0", CVAR_BOOL, CVAR_CHEAT, "Show render-to-texture statistics", 0, 0);

	// Debug tool variables
	r_showDepth = CVar_Register("r_showDepth", "0", CVAR_BOOL, CVAR_CHEAT, "Draw the contents of the depth buffer", 0, 0);
	r_showOverdraw = CVar_Register("r_showOverdraw", "0", CVAR_INTEGER, CVAR_CHEAT, "Draw triangles colored by overdraw (1 = ambient, 2 = interaction, 3 = ambient and interaction)", 0, 3);
	r_showShadowCount = CVar_Register("r_showShadowCount", "0", CVAR_INTEGER, CVAR_CHEAT, "Draw surfaces colored by shadow count (1 = count all, 2 = only count static shadows, 3 = only count dynamic shadows)", 0, 3);
	r_showLightCount = CVar_Register("r_showLightCount", "0", CVAR_INTEGER, CVAR_CHEAT, "Draw triangles colored by light count (1 = count visible ones, 2 = count everything through walls)", 0, 2);
	r_showLightVolumes = CVar_Register("r_showLightVolumes", "0", CVAR_INTEGER, CVAR_CHEAT, "Draw light volumes (1 = draw planes, 2 = draw edges, 3 = draw planes and edges)", 0, 3);
	r_showLightScissors = CVar_Register("r_showLightScissors", "0", CVAR_BOOL, CVAR_CHEAT, "Draw light scissor rectangles", 0, 0);
	r_showShadowTris = CVar_Register("r_showShadowTris", "0", CVAR_INTEGER, CVAR_CHEAT, "Draw shadows in wireframe mode (1 = draw visible ones, 2 = draw everything through walls)", 0, 2);
	r_showShadowVolumes = CVar_Register("r_showShadowVolumes", "0", CVAR_BOOL, CVAR_CHEAT, "Draw shadow planes", 0, 0);
	r_showShadowSilhouettes = CVar_Register("r_showShadowSilhouettes", "0", CVAR_BOOL, CVAR_CHEAT, "Draw shadow silhouettes", 0, 0);
	r_showTris = CVar_Register("r_showTris", "0", CVAR_INTEGER, CVAR_CHEAT, "Draw triangles in wireframe mode (1 = draw visible ones, 2 = draw everything through walls)", 0, 2);
	r_showNormals = CVar_Register("r_showNormals", "0.0", CVAR_FLOAT, CVAR_CHEAT, "Draw vertex normals", 0.0f, 100.0f);
	r_showTextureVectors = CVar_Register("r_showTextureVectors", "0.0", CVAR_FLOAT, CVAR_CHEAT, "Draw texture (tangent) vectors", 0.0f, 100.0f);
	r_showTangentSpace = CVar_Register("r_showTangentSpace", "0", CVAR_INTEGER, CVAR_CHEAT, "Draw triangles colored by tangent space (1 = 1st tangent, 2 = 2nd tangent, 3 = normal)", 0, 3);
	r_showTextureCoords = CVar_Register("r_showTextureCoords", "0", CVAR_BOOL, CVAR_CHEAT, "Draw triangles colored by texture coords", 0, 0);
	r_showVertexColors = CVar_Register("r_showVertexColors", "0", CVAR_BOOL, CVAR_CHEAT, "Draw triangles colored by vertex colors", 0, 0);
	r_showBatchSize = CVar_Register("r_showBatchSize", "0", CVAR_INTEGER, CVAR_CHEAT, "Draw triangles colored by batch size (1 = draw visible ones, 2 = draw everything through walls)", 0, 2);
	r_showModelBounds = CVar_Register("r_showModelBounds", "0", CVAR_INTEGER, CVAR_CHEAT, "Draw model bounds (1 = draw a filled box, 2 = draw a box with lines, 3 = draw with both", 0, 3);
	r_showColorMipLevels = CVar_Register("r_showColorMipLevels", "0", CVAR_BOOL, CVAR_CHEAT | CVAR_LATCH, "Color mip levels for testing mipmap usage", 0, 0);

	// Performance and debug tools variables
	r_skipVisibility = CVar_Register("r_skipVisibility", "0", CVAR_BOOL, CVAR_CHEAT, "Skip visibility determination tests", 0, 0);
	r_skipAreas = CVar_Register("r_skipAreas", "0", CVAR_BOOL, CVAR_CHEAT, "Skip area portals and render everything behind closed doors", 0, 0);
	r_skipCulling = CVar_Register("r_skipCulling", "0", CVAR_INTEGER, CVAR_CHEAT, "Skip culling for debugging (1 = boxes, 2 = spheres, 3 = both", 0, 3);
	r_skipSuppress = CVar_Register("r_skipSuppress", "0", CVAR_BOOL, CVAR_CHEAT, "Skip per-view suppressions", 0, 0);
	r_skipLightScissors = CVar_Register("r_skipLightScissors", "0", CVAR_BOOL, CVAR_CHEAT, "Skip scissor testing when rendering lights", 0, 0);
	r_skipDepthBounds = CVar_Register("r_skipDepthBounds", "1", CVAR_BOOL, CVAR_CHEAT, "Skip depth bounds testing when rendering shadows", 0, 0); // FIXME: set default to 0
	r_skipLights = CVar_Register("r_skipLights", "0", CVAR_BOOL, CVAR_CHEAT, "Skip rendering lights", 0, 0);
	r_skipBump = CVar_Register("r_skipBump", "0", CVAR_BOOL, CVAR_CHEAT, "Skip rendering bump stages", 0, 0);
	r_skipDiffuse = CVar_Register("r_skipDiffuse", "0", CVAR_BOOL, CVAR_CHEAT, "Skip rendering diffuse stages", 0, 0);
	r_skipSpecular = CVar_Register("r_skipSpecular", "0", CVAR_BOOL, CVAR_CHEAT, "Skip rendering specular stages", 0, 0);
	r_skipCopyToTextures = CVar_Register("r_skipCopyToTextures", "0", CVAR_BOOL, CVAR_CHEAT, "Skip copying the framebuffer to textures", 0, 0);
	r_skipDynamicTextures = CVar_Register("r_skipDynamicTextures", "0", CVAR_BOOL, CVAR_CHEAT, "Skip updating dynamically generated textures", 0, 0);


	Cmd_AddCommand("screenshot", R_Screenshot_f);
	Cmd_AddCommand("envShot", R_EnvShot_f);
	Cmd_AddCommand("gfxInfo", R_GfxInfo_f);
	Cmd_AddCommand("listVideoModes", R_ListVideoModes_f);
	Cmd_AddCommand("listShaders", R_ListShaders_f);
	Cmd_AddCommand("listPrograms", R_ListPrograms_f);
	Cmd_AddCommand("listTextures", R_ListTextures_f);
	Cmd_AddCommand("listTables", R_ListTables_f);
	Cmd_AddCommand("listMaterials", R_ListMaterials_f);
}

/*
 ==================
 R_UnregisterConfig
 ==================
*/
static void R_UnregisterConfig ()
{
	Cmd_RemoveCommand("screenshot");
	Cmd_RemoveCommand("envShot");
	Cmd_RemoveCommand("gfxInfo");
	Cmd_RemoveCommand("listVideoModes");
	Cmd_RemoveCommand("listShaders");
	Cmd_RemoveCommand("listPrograms");
	Cmd_RemoveCommand("listTextures");
	Cmd_RemoveCommand("listTables");
	Cmd_RemoveCommand("listMaterials");
}


/*
 ==============================================================================

    FRAMEWORK

 ==============================================================================
*/

void R_PerformanceCounters (); // FIXME!!!

/*
 ==================
 R_BeginFrame
 ==================
*/
void R_BeginFrame ()
{
	drawBufferCommand_t   *cmd;
	
	// Check log file
	if (r_logFile->modified)
	{
		QGL_EnableLogging(r_logFile->integerValue);

		r_logFile->modified = false;
	}

	// Bump frame count
	rg.frameCount++;

	// Clear performance counters
	memset(&rg.pc, 0, sizeof(performanceCounters_t));

	// Clear mesh, light and scene lists
	R_ClearMeshLists();
	R_ClearLightMeshLists();
	R_ClearSceneLists();

	// Set the crop size
	R_SetCropSize();

	// Set default GL state
	GL_SetDefaultState();

	// Update gamma table and device gamma ramp if needed
	if (r_gamma->modified || r_contrast->modified || r_brightness->modified)
	{
		R_SetGamma();

		r_gamma->modified = false;
		r_contrast->modified = false;
		r_brightness->modified = false;
	}

	// Update texture parameters if needed
	if (r_textureFilter->modified || r_textureLODBias->modified || r_textureAnisotropy->modified)
	{
		R_UpdateTextureParameters();

		r_textureFilter->modified = false;
		r_textureLODBias->modified = false;
		r_textureAnisotropy->modified = false;
	}

	// Clear all commands
	RB_ClearRenderCommands();

	// Add a draw buffer command
	cmd = (drawBufferCommand_t *)RB_GetCommandBuffer(sizeof(*cmd));
	if (cmd)
	{
		cmd->commandId = RC_DRAW_BUFFER;

		cmd->width = rg.currentRenderCrop->width;
		cmd->height = rg.currentRenderCrop->height;
	}
}

/*
 ==================
 R_EndFrame
 ==================
*/
void R_EndFrame ()
{
	swapBuffersCommand_t   *cmd;

	// Performance counters
	R_PerformanceCounters();

	// Add a swap buffers command
	cmd = (swapBuffersCommand_t *)RB_GetCommandBuffer(sizeof(*cmd));
	if (cmd)
		cmd->commandId = RC_SWAP_BUFFERS;

	// Issue all commands
	RB_IssueRenderCommands();

	// Reset log file
	if (r_logFile->integerValue > 0)
		CVar_SetInteger(r_logFile, r_logFile->integerValue - 1);
}


/*
 ==============================================================================

    INITIALIZATION AND SHUTDOWN

 ==============================================================================
*/

void R_ClearRender (); // FIXME!!!


/*
 ==================
 R_Init
 ==================
*/
void R_Init (bool all)
{
	Com_Printf("-------- Renderer Initialization --------\n");

	if (all)
	{
		// Register variables to config
		R_RegisterConfig();

		// Create the GL window
		GLImp_Init();
	}

	// Set default GL state
	GL_SetDefaultState();

	// Clear structure data
	R_ClearRender();

	// Create memory pools
	rg.shaderMemoryPool = Mem_RegisterPool("Renderer: Shader system");
	rg.textureMemoryPool = Mem_RegisterPool("Renderer: Texture system");
	rg.materialMemoryPool = Mem_RegisterPool("Renderer: Material system");
	rg.fontMemoryPool = Mem_RegisterPool("Renderer: Font system");
	rg.modelMemoryPool = Mem_RegisterPool("Renderer: Model system");
	rg.lightMemoryPool = Mem_RegisterPool("Renderer: Light system");
	rg.debugMemoryPool = Mem_RegisterPool("Renderer: Debug system");

	// Build gamma table and set device gamma ramp
	R_SetGamma();

	// Initialize front-end modules
	R_InitShaders();
	R_InitTextures();
	R_InitMaterials();
	R_InitFonts();
	R_InitModels();
	R_InitLightEditor();
	R_InitGeneratorEditor();
	R_InitNavigationEditor();
	R_InitScene();

	// Initialize back-end module
	RB_InitBackEnd();

	if (!r_ignoreGLErrors->integerValue)
		GL_CheckForErrors();

	Com_Printf("---------------------------------------\n");
}

/*
 ==================
 R_Shutdown
 ==================
*/
void R_Shutdown (bool all)
{
	if (!glConfig.initialized)
		return;

	// Shutdown back-end module
	RB_ShutdownBackEnd();

	// Shutdown front-end modules
	R_ShutdownScene();
	R_ShutdownNavigationEditor();
	R_ShutdownGeneratorEditor();
	R_ShutdownLightEditor();
	R_ShutdownModels();
	R_ShutdownFonts();
	R_ShutdownMaterials();
	R_ShutdownTextures();
	R_ShutdownShaders();

	// Free memory pools
	Mem_FreePool(rg.shaderMemoryPool);
	Mem_FreePool(rg.textureMemoryPool);
	Mem_FreePool(rg.materialMemoryPool);
	Mem_FreePool(rg.fontMemoryPool);
	Mem_FreePool(rg.modelMemoryPool);
	Mem_FreePool(rg.lightMemoryPool);
	Mem_FreePool(rg.debugMemoryPool);

	if (all)
	{
		// Shutdown the GL window
		GLImp_Shutdown();

		// Unregister commands
		R_UnregisterConfig();
	}
}